﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.IO;
using MySql.Data.MySqlClient;
using MySql.Data.MySqlClient.Memcached;
using MySqlX.XDevAPI.Common;
using MySqlX.XDevAPI.Relational;

namespace myMapObj
{
    enum ShapeType
    {
        NullShape, Point, Polyline, Polygon, MultiPoint, PointZ, PolyLineZ, PolygonZ, MultiPointZ, PointM, PolyLineM, PolygonM, MultiPointM, MultiPatch
    }

    public class Shapefile
    {
        #region 数据库连接
        string connectString = "Data Source=localhost;Database=mygis;User ID=root;Password=xiaoxiao2333..;Charset=utf8;Allow User Variables=True;SslMode=None";
        MySqlConnection cnn = null;


        #endregion

        #region 字段
        string fileName;
        string filePath;
        //string attrFilePath;
        string tableName;
        moGeometryTypeConstant _GeometryType;
        PointF centerPos;

        int fileCode;
        int fileLength;   //文件长度，单位：字节
        int version;      //版本号
        int shapeTypeID;  //几何类型
        ShapeType shapeType;
        double _MinX, _MinY, _MaxX, _MaxY, _MinZ, _MaxZ, _MinM, _MaxM;
        int byteCount = 0;  //已经读取的字节数
        List<moPoint> _Points;
        List<moMultiPolyline> _MultiPolylines;
        List<moMultiPolygon> _MultiPolygons;
        moFeatures _Features;
        #endregion

        #region 属性
        public string Name
        {
            get { return fileName; }
        }
        public string FilePath
        {
            get { return filePath; }
        }
        /*public string AttrFilePath
        {
            get { return attrFilePath; }
        }*/
        public string TableName
        {
            get { return tableName; }
        }
        public moGeometryTypeConstant GeometryType
        {
            get { return _GeometryType; }
        }
        public double MinX
        {
            get { return _MinX; }
        }
        public double MinY
        {
            get { return _MinY; }
        }
        public double MaxX
        {
            get { return _MaxX; }
        }
        public double MaxY
        {
            get { return _MaxY; }
        }
        /// <summary>
        /// 中心经纬度坐标
        /// </summary>
        internal PointF CenterPos
        {
            get { return centerPos; }
        }
        #endregion

        #region 构造函数
        public Shapefile(string filePath)
        {
            //cnn = new MySqlConnection(connectString);
            this.filePath = filePath;
            fileName = Path.GetFileNameWithoutExtension(filePath);
            //attrFilePath = Path.GetDirectoryName(filePath) + "\\" + fileName + ".mdb";
            tableName = fileName;
            FileStream fs = new FileStream(filePath, FileMode.Open);
            BinaryReader br = new BinaryReader(fs);

            //读取文件头
            fileCode = ReverseByte(br.ReadInt32());
            for (int i = 1; i <= 5; i++)
                br.ReadInt32();
            fileLength = ReverseByte(br.ReadInt32()) * 2;
            version = br.ReadInt32();
            shapeTypeID = br.ReadInt32();
            _MinX = br.ReadDouble();
            _MinY = br.ReadDouble();
            _MaxX = br.ReadDouble();
            _MaxY = br.ReadDouble();
            _MinZ = br.ReadDouble();
            _MaxZ = br.ReadDouble();
            _MinM = br.ReadDouble();
            _MaxM = br.ReadDouble();
            byteCount += 100;

            float xcenter = ((float)_MinX + (float)_MaxX) / 2;
            float ycenter = ((float)_MinY + (float)_MaxY) / 2;
            centerPos = new PointF(xcenter, ycenter);

            switch (shapeTypeID)
            {
                case 1:
                    {
                        shapeType = ShapeType.Point;
                        ReadPointShp(br);
                        _GeometryType = moGeometryTypeConstant.Point;
                        break;
                    }
                case 3:
                    {
                        shapeType = ShapeType.Polyline;
                        ReadPolylineShp(br);
                        _GeometryType = moGeometryTypeConstant.MultiPolyline;
                        break;
                    }
                case 5:
                    {
                        shapeType = ShapeType.Polygon;
                        ReadPolygonShp(br);
                        _GeometryType = moGeometryTypeConstant.MultiPolygon;
                        break;
                    }
                default:
                    {
                        shapeType = ShapeType.NullShape;
                        break;
                    }
            }
        }
        #endregion

        #region 私有函数

        #region 私有函数
        private void ReadPointShp(BinaryReader br)
        {
            _Points = new List<moPoint>();
            int recordNumber;  //记录号
            int contentLength;  //坐标记录长度
            int shapeType;  //几何类型
            double x, y;
            while (byteCount < fileLength)
            {
                recordNumber = ReverseByte(br.ReadInt32());
                contentLength = ReverseByte(br.ReadInt32());
                shapeType = br.ReadInt32();
                x = br.ReadDouble();
                y = br.ReadDouble();
                moPoint p = new moPoint(x, y);
                MercatoProjection projection = new MercatoProjection();
                p = projection.LngLat2XY(p);
                _Points.Add(p);
                byteCount += 28;
            }
        }

        private void ReadPolylineShp(BinaryReader br)
        {
            _MultiPolylines = new List<moMultiPolyline>();
            while (byteCount < fileLength)
            {
                moMultiPolyline temp = new moMultiPolyline(br);
                byteCount += temp.ByteCount;
                _MultiPolylines.Add(temp);
            }
        }
        private void ReadPolygonShp(BinaryReader br)
        {
            _MultiPolygons = new List<moMultiPolygon>();
            while (byteCount < fileLength)
            {
                moMultiPolygon temp = new moMultiPolygon(br);
                byteCount += temp.ByteCount;
                _MultiPolygons.Add(temp);
            }
        }
        static void ExchangeByte(ref byte b1, ref byte b2)
        {
            byte temp;
            temp = b1;
            b1 = b2;
            b2 = temp;
        }
        #endregion

        /// <summary>
        /// 大尾整数转小尾整数
        /// </summary>
        /// <param name="big">大尾整数</param>
        /// <returns>小尾整数</returns>
        public static int ReverseByte(int big)
        {
            byte[] bytes = BitConverter.GetBytes(big);
            ExchangeByte(ref bytes[0], ref bytes[3]);
            ExchangeByte(ref bytes[1], ref bytes[2]);
            int little = BitConverter.ToInt32(bytes, 0);
            return little;
        }

        public moGeometry[] GetGeometryFeature()
        {
            moGeometry[] sGeometries = null;
            switch (_GeometryType)
            {
                case moGeometryTypeConstant.Point:
                    sGeometries = _Points.ToArray();
                    break;
                case moGeometryTypeConstant.MultiPolyline:
                    sGeometries = _MultiPolylines.ToArray();
                    break;
                case moGeometryTypeConstant.MultiPolygon:
                    sGeometries = _MultiPolygons.ToArray();
                    break;
                default:
                    break;
            }
            return sGeometries;
        }

        private moFields LoadFields()
        {
            cnn = new MySqlConnection(connectString);
            cnn.Open();
            string query_column = "select COLUMN_NAME,DATA_TYPE from information_schema.COLUMNS where table_name = '" + tableName + "'; ";
            MySqlCommand cmd1 = null;
            MySqlDataReader dataReader1 = null;
            cmd1 = new MySqlCommand();
            cmd1.Connection = cnn;
            cmd1.CommandText = query_column;
            dataReader1 = cmd1.ExecuteReader();
            moFields sFields = new moFields();

            while (dataReader1.Read())
            {
                string name = dataReader1.GetString(0);
                string type = dataReader1.GetString(1);
                moField sField = null;
                if (type == "int")
                {
                    sField = new moField(name, moValueTypeConstant.dInt32);
                }

                else if (type == "varchar")
                {
                    sField = new moField(name, moValueTypeConstant.dText);
                }
                else if (type == "float")
                {
                    sField = new moField(name, moValueTypeConstant.dSingle);
                }
                else if (type == "double")
                {
                    sField = new moField(name, moValueTypeConstant.dDouble);
                }
                else if (type == "text")
                {
                    sField = new moField(name, moValueTypeConstant.dText);
                }
                sFields.Append(sField);

            }
            dataReader1.Close();
            return sFields;
        }

        private moFeatures LoadFeatures(moFields fields)
        {
            moFeatures sFeatures = new moFeatures();
            Int32 sFeatureCount = GetGeometryFeature().Count();
            for (int i = 0; i <= sFeatureCount - 1; i++)
            {
                moGeometry sGeometry = GetGeometryFeature()[i];
                moAttributes sAttributes = LoadAttributes(fields, i);
                moFeature sFeature = new moFeature(_GeometryType, sGeometry, sAttributes);
                sFeatures.Add(sFeature);
            }
            return sFeatures;
        }

        private moAttributes LoadAttributes(moFields fields, int index)
        {
            Int32 sFieldCount = fields.Count;
            moAttributes sAttributes = new moAttributes();
            for (Int32 i = 0; i <= sFieldCount - 1; i++)
            {
                moField sField = fields.GetItem(i);
                object sValue = LoadValue(sField.ValueType, sField, index);
                sAttributes.Append(sValue);
            }
            return sAttributes;
        }

        private object LoadValue(moValueTypeConstant ValueType, moField field, int i)
        {
            cnn = new MySqlConnection(connectString);
            cnn.Open();
            string query_column = "select " + field.Name + " from mygis." + tableName + " where FID=" + i.ToString() + "; ";
            MySqlCommand cmd1 = null;
            MySqlDataReader dataReader1 = null;
            cmd1 = new MySqlCommand();
            cmd1.Connection = cnn;
            cmd1.CommandText = query_column;
            dataReader1 = cmd1.ExecuteReader();
            moFields sFields = new moFields();
            object value = new object();

            while (dataReader1.Read())
            {
                if (ValueType == moValueTypeConstant.dInt32)
                {
                    value = dataReader1.GetInt32(0);
                }
                else if (ValueType == moValueTypeConstant.dDouble)
                {
                    value = dataReader1.GetDouble(0);
                }
                else if (ValueType == moValueTypeConstant.dSingle)
                {
                    value = dataReader1.GetFloat(0);
                }
                else if (ValueType == moValueTypeConstant.dText)
                {
                    value = dataReader1.GetString(0);
                }
            }
            dataReader1.Close();
            return value;
        }

        public moMapLayer LoadMapLayer()
        {
            moFields sFields = LoadFields();
            moFeatures sFeatures = LoadFeatures(sFields);
            moMapLayer sMapLayer = new moMapLayer(fileName, _GeometryType, sFields);
            sMapLayer.Features = sFeatures;
            return sMapLayer;
        }
        #endregion
    }
}
